package lab_15.com.acme.bankapp.domain.bank.Email;

import java.util.*;

public class EmailQueue {
	private final List<Email> queue = new LinkedList<Email>();
	// Different threads may be locking on different objects.
	// [ogolovin] But I used closingMode to set a status of closing on runtime
	// (in close() method). So it shouldn't be constant, as it changes on
	// runtime.
	// I completely refactored the code.
	// I got rid of synchronized sections on closingMode and used volatile
	// instead (it's OK
	// as atomic should be either read or write (not the set of operations),
	// which can be achieved with the use of volatile).
	// I decided not to keep the main thread waiting for all the senders
	// to shut down.
	// We can leave them sending the rest of the messages and getting
	// terminating on themselves without
	// keeping the main thread waiting for them to perform these operations.
	private final Thread sender = new Sender();
	private volatile boolean closingMode = false;

	public EmailQueue() {
		sender.start();
	}

	class Sender extends Thread {
		public void run() {
			while (true) {
				try {
					Email email = getEmail();
					email.send();
				} catch (InterruptedException e) { // interrupted status was set
													// while sender was waiting
					System.out.println("[run] Sender is about to teminate.");
					return; // stop thread
				}

			}
		}
	}

	/**
	 * Get an e-mail from the queue.
	 * 
	 * @return e-mail
	 * @throws InterruptedException
	 */
	private Email getEmail() throws InterruptedException {
		synchronized (queue) {
			while (queue.isEmpty()) {
				if (closingMode) {
					Thread.currentThread().interrupt(); // Interruption will
														// happen on entering
														// wait.
				}
				queue.wait(); // put sender to waiting for another e-mail
				// Thread will interrupt on entering wait or while waiting on
				// iterruption.
			}
			return queue.remove(0);
		}
	}

	/**
	 * Add e-mail to the queue
	 * 
	 * @param email
	 *            e-mail to add to the queue for sending
	 * @exception IllegalStateException
	 *                Thrown when the queue is closed
	 */
	public void add(Email email) {
		if (closingMode) {
			throw new IllegalStateException(
					"[add] Closing mode! Email service is stopped and doesn't accept new messages.");
		}
		synchronized (queue) {
			queue.add(email);
			queue.notify();
		}
	}

	/**
	 * Stop the sender. All the e-mails in the queue will be sent before
	 * stopping. No new messages will be accepted via {@link #add(Email email)
	 * add} method.
	 */
	public void close() {
		closingMode = true;
		synchronized (queue) {
			queue.notifyAll(); // ping all the threads to wake up and try
								// terminating
		}

		// The code bellow is just for testing. Should be removed in the real
		// code
		try {
			sender.join(); // Wait for a sender to die.
		} catch (InterruptedException e) {
			e.printStackTrace(); // We will never send interruption to the main
									// thread, so just printing stack if it has
									// for some obscure reason happened
		}
		System.out.println("[close] Sender has terminated itself.");
	}
}
